import asyncio
from typing import List, Tuple

from aiogram import Bot, exceptions
from aiogram.types import InputFile

from app.bot.utils.logger import log
from app.database.models import User


async def send_message(
    bot: Bot, user_id, text: str, disable_notification: bool = False
) -> bool:

    try:
        await bot.send_message(user_id, text, disable_notification=disable_notification)
    except exceptions.TelegramForbiddenError:
        log.error(f"Target [ID:{user_id}]: got TelegramForbiddenError")
    except exceptions.TelegramRetryAfter as e:
        log.error(
            f"Target [ID:{user_id}]: Flood limit is exceeded. Sleep {e.retry_after} seconds."
        )
        await asyncio.sleep(e.retry_after)
        return await send_message(bot, user_id, text)  # Recursive call
    except exceptions.TelegramAPIError:
        log.exception(f"Target [ID:{user_id}]: failed")
    else:
        log.info(f"Target [ID:{user_id}]: success")
        return True
    return False


async def broadcast(
    bot: Bot,
    users: List[int | User],
    text: str,
) -> Tuple[int, int]:
    """
    Simple broadcaster
    :param bot: aiogram.Bot instance
    :param text: Text to send
    :param users: list of telegram ids or User instances
    :return: Messages succesfully sent, messages with errors
    """
    messages_sent = 0
    errors_count = 0
    try:
        for user in users:
            if isinstance(user, User):
                user = user.telegram_id
            if await send_message(bot, user, text):
                messages_sent += 1
            else:
                errors_count += 1
            await asyncio.sleep(
                0.05
            )  # 20 messages per second (Limit: 30 messages per second)
    finally:
        log.info(
            f"{messages_sent} messages successful sent. With errors: {errors_count}. Planned to send total: {len(users)}"
        )

    return messages_sent, errors_count


async def broadcast_document(
    bot: Bot, telegram_id: int, file: InputFile, **kwargs
) -> None:
    try:
        await bot.send_document(telegram_id, file, **kwargs)
        log.info(f"Отправил документ юзеру {telegram_id}")
    except (exceptions.TelegramForbiddenError, exceptions.TelegramRetryAfter):
        log.info(f"Не могу отправить файл пользователю {telegram_id}")
